package com.shop.cereshop.admin.service.online.impl;

import com.shop.cereshop.admin.redis.service.UserRedisService;
import com.shop.cereshop.admin.service.buyer.CereBuyerUserService;
import com.shop.cereshop.admin.service.online.OnlineUserService;
import com.shop.cereshop.commons.config.SecurityProperties;
import com.shop.cereshop.commons.constant.RedisConstants;
import com.shop.cereshop.commons.domain.buyer.CereBuyerUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class OnlineUserServiceImpl implements OnlineUserService {

    @Resource
    private CereBuyerUserService cereBuyerUserService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private SecurityProperties securityProperties;

    @Autowired
    private UserRedisService userRedisService;

    // 用户上线Lua脚本
    private final DefaultRedisScript<Long> userOnlineScript;

    // 清理过期用户Lua脚本
    private final DefaultRedisScript<Long> cleanExpiredUsersScript;

    // 用户下线Lua脚本
    private final DefaultRedisScript<Long> userOfflineScript;

    public OnlineUserServiceImpl() {
        // 用户上线脚本 (简化，因为用户信息现在通过RedisTemplate自动序列化)
        userOnlineScript = new DefaultRedisScript<>();
        userOnlineScript.setScriptText(
                "redis.call('ZADD', KEYS[1], ARGV[1], ARGV[2])\n" +
                        "return 1"
        );
        userOnlineScript.setResultType(Long.class);

        // 清理过期用户脚本
        cleanExpiredUsersScript = new DefaultRedisScript<>();
        cleanExpiredUsersScript.setScriptText(
                "local current_time = tonumber(ARGV[1])\n" +
                        "local expired_count = redis.call('ZREMRANGEBYSCORE', KEYS[1], 0, current_time)\n" +
                        "return expired_count"
        );
        cleanExpiredUsersScript.setResultType(Long.class);

        // 用户下线脚本
        userOfflineScript = new DefaultRedisScript<>();
        userOfflineScript.setScriptText(
                "local removed = redis.call('ZREM', KEYS[1], ARGV[1])\n" +
                        "if removed > 0 then\n" +
                        "  redis.call('DEL', KEYS[2])\n" +
                        "end\n" +
                        "return removed"
        );
        userOfflineScript.setResultType(Long.class);
    }

    @Override
    public void userOffline(String token) {
        // 通过token获取用户ID
        Long buyerUserId = userRedisService.getBuyerUserIdByToken(token);

        if (buyerUserId != null) {
            // 从在线用户列表中移除用户
            List<String> keys = Arrays.asList(
                    RedisConstants.ONLINE_USER_KEY,
                    RedisConstants.USER_INFO_PREFIX + buyerUserId
            );

            List<String> args = Collections.singletonList(buyerUserId.toString());

            // 删除用户信息
            redisTemplate.delete(RedisConstants.USER_INFO_PREFIX + buyerUserId);

            // 执行Lua脚本从在线列表中移除用户
            redisTemplate.execute(userOfflineScript, keys, args.toArray());
        }
    }

    @Override
    public int getOnlineUserCount() {
        // 返回在线用户数量
        return redisTemplate.opsForZSet().zCard(RedisConstants.ONLINE_USER_KEY).intValue();
    }

    @Override
    public List<CereBuyerUser> getOnlineUsers() {
        // 获取所有在线用户ID
        Set<String> userIds = redisTemplate.opsForZSet().range(RedisConstants.ONLINE_USER_KEY, 0, -1);

        if (userIds == null || userIds.isEmpty()) {
            return Collections.emptyList();
        }

        // 批量获取用户信息从Hash中读取
        List<Object> userInfoList = redisTemplate.opsForHash().multiGet(
            RedisConstants.USER_INFO_PREFIX, 
            new ArrayList<>(userIds)
        );

        return userInfoList.stream()
            .filter(Objects::nonNull)
            .map(user -> (CereBuyerUser) user)
            .collect(Collectors.toList());
    }
}
